#include "router.h"
#include "tunnel.h"
#include "client.h"
#include "../common/codec.h"
#include "../common/factory.h"
#include "../common/console.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>



Router::Router (Client* client, const Config& cfg) : _cfg(cfg), _client(client) {
  TRACE
}


void Router::startup (Tunnel* tunnel) {
  TRACE
  _interface = open(_cfg.tun_dev, O_RDWR);
  abort_assert(_interface > 0);

  struct ifreq ifr = {0};
  memset(&ifr, 0, sizeof(ifr));
  ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
  strcpy(ifr.ifr_name, _cfg.virtual_if);
  abort_assert(-1 != ioctl(_interface, TUNSETIFF, &ifr));

  Console::run("ifconfig %s %s up", _cfg.virtual_if, _cfg.virtual_ip);
  Console::run("route add default dev %s", _cfg.virtual_if);
//  Console::run("iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination %s:53", _cfg.virtual_ip);
//  Console::run("iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to-destination %s:53", _cfg.virtual_ip);

  _tunnel = tunnel;
  _upWorker = new std::thread(&Router::upLoop, this);
  _upWorker->detach();
  _downWorker = new std::thread(&Router::downLoop, this);
  _downWorker->detach();
}


void Router::shutdown () {
  TRACE
  Console::run("route del default dev %s", _cfg.virtual_if);
//  Console::run("iptables -t nat -D OUTPUT -p udp --dport 53 -j DNAT --to-destination %s:53", _cfg.virtual_ip);
//  Console::run("iptables -t nat -D OUTPUT -p tcp --dport 53 -j DNAT --to-destination %s:53", _cfg.virtual_ip);
  delete _upWorker;
  delete _downWorker;
  close(_interface);
}


void Router::upLoop () {
  TRACE
  u8 buf[IpPackage::MAX_SIZE];
  for (;;) {
    auto nread = read(_interface, buf, IpPackage::MAX_SIZE);
    if (nread <= 0) {
      log_e("Router: read data error");
      Factory::executor().execute([this] () {
        _client->shutdown();
      });
      return;
    }
    _cache.write(buf, (uwl)nread);
    auto pkg = Codec::parse(_cache);
//    auto pkg = Ptr<IpPackage>::create();
//    pkg->buf = ByteBuffer<>::create(buf, (uwl)nread);
    if (!pkg->err.empty())
      log_e("Router: package error: %p", pkg.ptr());
    if (pkg)
      _tunnel->write(std::move(pkg));
  }
}


void Router::downLoop () {
  TRACE
  for (;;) {
    auto pkg = _queue.poll();
    auto nwrite = ::write(_interface, pkg->buf.readIter(), pkg->buf.readable());
    if (nwrite <= 0) {
      log_e("Router: write data error");
      Factory::executor().execute([this] () {
        _client->shutdown();
      });
      return;
    }
  }
}


void Router::write (Ptr<IpPackage> pkg) {
  TRACE
  _queue.push(std::move(pkg));
}